package com.amoyt.project.aop;

import com.amoyt.project.annotations.BusinessLog;
import com.amoyt.project.annotations.ModuleLog;
import com.amoyt.project.domain.bo.LoginUser;
import com.amoyt.project.domain.pojo.OperationLog;
import com.amoyt.project.domain.res.Result;
import com.amoyt.project.mapper.OperationLogMapper;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import jakarta.servlet.http.HttpServletRequest;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AnonymousAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.multipart.MultipartFile;

import java.lang.reflect.Method;
import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.Objects;

/**
 * 操作日志切面（基于Jackson序列化）
 */
@Aspect
@Component
public class OperationLogAspect {

    @Autowired
    private OperationLogMapper operationLogMapper;

    @Autowired
    private ObjectMapper objectMapper; // Jackson的ObjectMapper

    /**
     * 切点：拦截所有标注了@BusinessLog注解的方法，且所在类标注了@ModuleLog注解
     */
    @Pointcut("@annotation(com.amoyt.project.annotations.BusinessLog) && @within(com.amoyt.project.annotations.ModuleLog)")
    public void logPointcut() {}

    /**
     * 环绕通知：记录操作日志
     */
    @Around("logPointcut()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        // 1. 初始化日志对象
        OperationLog log = new OperationLog();
        log.setOperationTime(LocalDateTime.now());

        // 2. 获取请求信息
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        if (attributes != null) {
            HttpServletRequest request = attributes.getRequest();
            // 获取IP地址
            log.setIpAddress(getIpAddress(request));
            // 获取客户端信息
            log.setUserAgent(request.getHeader("User-Agent"));
            // 获取API路径（业务名称）
            log.setRequestPath(request.getRequestURI());
            // 根据HTTP方法推断操作类型
            log.setOperationType(getOperationType(request.getMethod()));
        }

        // 3. 获取注解信息（模块+操作名称）
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        // 获取类上的模块注解
        ModuleLog moduleLog = joinPoint.getTarget().getClass().getAnnotation(ModuleLog.class);
        log.setBusinessModule(moduleLog.module());
        // 获取方法上的操作注解
        BusinessLog businessLog = method.getAnnotation(BusinessLog.class);
        log.setBusinessName(businessLog.business());
        //看有没有自行指定操作类型  操作类型（如：1-新增 2-修改 3-删除 4-查询 5-登录 6-登出 7-导入 8-导出 9-其他）
        if(businessLog.type()!=null && ! businessLog.type().equals("")){
            log.setOperationType(Integer.valueOf(businessLog.type()));
        }


        // 4. 获取用户信息（从SecurityContextHolder）
        setUserInfo(log);

        // 5. 处理方法参数（使用Jackson序列化）
        log.setMethodParams(getParamsJson(joinPoint));

        Result result = null;
        try {
            // 执行目标方法
            result = (Result) joinPoint.proceed();
            // 6. 记录成功结果
            if(result.getCode() == 1){
                log.setOperationResult(1); // 1-成功
            }else {
                log.setOperationResult(0); // 0-失败
            }

            log.setResultData(getResultJson(result,log.getOperationType()));
            return result;
        } catch (Exception e) {
            // 7. 记录异常信息
            log.setOperationResult(0); // 0-失败
            log.setErrorMsg(getExceptionMsg(e));
            throw e; // 继续抛出异常，不影响原有业务逻辑
        } finally {
            // 8. 保存日志到数据库
            operationLogMapper.insert(log);
        }
    }

    /**
     * 获取操作用户信息（从SecurityContextHolder）
     */
    private void setUserInfo(OperationLog log) {
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        if (authentication != null && !(authentication instanceof AnonymousAuthenticationToken)) {
            // 假设用户详情实现类为UserDetailsImpl，根据实际项目调整
            LoginUser loginUser = (LoginUser) authentication.getPrincipal();
            log.setOperatorId(loginUser.getUser().getUserId());
            log.setOperatorName(loginUser.getUser().getUserName());
        } else {
            // 匿名用户（未登录）
            log.setOperatorId("-1");
            log.setOperatorName("匿名用户");
        }
    }

    /**
     * 将方法参数转为JSON字符串（使用Jackson）
     */
    private String getParamsJson(ProceedingJoinPoint joinPoint) {
        try {
            Object[] args = joinPoint.getArgs();
            if (args == null || args.length == 0) {
                return "[]";
            }
            // 过滤MultipartFile等特殊类型
            Object[] filteredArgs = Arrays.stream(args)
                    .filter(arg -> !(arg instanceof MultipartFile) 
                            && !(arg instanceof HttpServletRequest)
                            && !(arg instanceof jakarta.servlet.http.HttpServletRequest))
                    .toArray();
            // 使用Jackson序列化
            return objectMapper.writeValueAsString(filteredArgs);
        } catch (JsonProcessingException e) {
            return "参数序列化失败：" + e.getMessage();
        }
    }

    /**
     * 将返回结果转为JSON字符串（使用Jackson）
     */
    private String getResultJson(Object result,Integer type) {
        if(type == 5){
            //不显示登录数据
            result = Result.success();
        }
        if (result == null) {
            return null;
        }
        try {
            // 限制结果长度，避免日志过大
            String json = objectMapper.writeValueAsString(result);
            return json.length() > 5000 ? json.substring(0, 5000) + "..." : json;
        } catch (JsonProcessingException e) {
            return "结果序列化失败：" + e.getMessage();
        }
    }

    /**
     * 获取异常信息（简化堆栈）
     */
    private String getExceptionMsg(Exception e) {
        String message = e.getMessage();
        if (message == null) {
            message = e.getClass().getSimpleName();
        }
        // 只保留简短异常信息，避免日志过大
        return message.length() > 500 ? message.substring(0, 500) + "..." : message;
    }

    /**
     * 根据HTTP方法获取操作类型
     */
    private Integer getOperationType(String httpMethod) {
        return switch (httpMethod.toUpperCase()) {
            case "POST" -> 1; // 新增
            case "PUT", "PATCH" -> 2; // 修改
            case "DELETE" -> 3; // 删除
            case "GET" -> 4; // 查询
            default -> 9; // 其他
        };
    }

    /**
     * 获取真实IP地址,尽量转换成IPv4
     */
    private String getIpAddress(HttpServletRequest request) {
        String ip = request.getHeader("x-forwarded-for");
        if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("Proxy-Client-IP");
        }
        if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("WL-Proxy-Client-IP");
        }
        if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddr();
        }

        // 处理多IP情况（取第一个）
        if (ip != null && ip.contains(",")) {
            ip = ip.split(",")[0].trim();
        }

        // 转换IPv6特殊地址为IPv4
        if (ip != null) {
            // 处理IPv6本地回环地址，转换为IPv4的127.0.0.1
            if ("0:0:0:0:0:0:0:1".equals(ip) || "::1".equals(ip)) {
                return "127.0.0.1";
            }

            // 处理IPv4映射的IPv6地址（格式如：::ffff:192.168.1.1）
            if (ip.startsWith("::ffff:")) {
                return ip.substring(7);
            }
        }

        return ip;
    }
}
    